package com.new4net.exe_stream.instruction.impl;

import com.new4net.exe_stream.instruction.BranchInstruction;
import com.new4net.exe_stream.listener.InstructionListener;
import com.new4net.exe_stream.meta.ExecutionContext;
import lombok.SneakyThrows;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

public class ParallelInstruction extends AbstractInstruction implements BranchInstruction<ExecutionInstruction> {
    private List<ExecutionInstruction> executionInstructions = new ArrayList<>();
    private boolean needWait;


    public ParallelInstruction() {
        super("_parallel");
    }

    public void setNeedWait(boolean needWait) {
        this.needWait = needWait;
    }
    public void addExecutionInstruction(ExecutionInstruction executionInstruction){
        executionInstructions.add(executionInstruction);
    }


    public ParallelInstruction addInstructionListener(InstructionListener instructionListener) {
        super.addInstructionListener(instructionListener);
        executionInstructions.forEach(instruction -> instruction.addInstructionListener(instructionListener));
        return this;
    }


    @Override
    public void setExecutionContext(ExecutionContext executionContext) {
        super.setExecutionContext(executionContext);


    }
    @Override
    protected void prev(){
        super.prev();
        copyContext();
    }

    private void copyContext() {
        //将上下文克隆给下级指令
        for(ExecutionInstruction executionInstruction:executionInstructions){
            ExecutionContext context = executionContext.clone();
            executionInstruction.setExecutionContext(context);
        }
    }

    @Override
    @SneakyThrows
    protected void exe() {
        ExecutorService executorService = executionContext.getExecutor();
        if(needWait){
            Map<String,Future<?>> futureMap = new HashMap<>();
            for(ExecutionInstruction instruction:executionInstructions){
                Future<?> submit = executorService.submit(instruction);
                futureMap.put(instruction.getInstructionName(),submit);
            }
            returnVal(futureMap);

        }else{
            for(ExecutionInstruction instruction:executionInstructions){
                executorService.execute(instruction);
            }
            this.retVal=null;
        }
        

    }

    @SneakyThrows
    protected void exeRetry() {
        ExecutorService executorService = executionContext.getExecutor();
        if(needWait){
            Map<String,Future<?>> futureMap = new HashMap<>();
            for(ExecutionInstruction instruction:executionInstructions){
                if("FAIL".equals(instruction.getExecuteResult().getStatus())){
                    Future<?> submit = executorService.submit(()->{
                        instruction.retry();
                    });
                    futureMap.put(instruction.getInstructionName(),submit);
                }

            }
            returnVal(futureMap);

        }else{
            for(ExecutionInstruction instruction:executionInstructions){
                if("FAIL".equals(instruction.getExecuteResult().getStatus())){
                    executorService.submit(()->instruction.retry());
                }
            }
            this.retVal=null;
        }

    }

    private void returnVal(Map<String, Future<?>> futureMap) throws InterruptedException, ExecutionException {
        Map<String,Object>  retVal = new HashMap<>();
        for(Map.Entry<String,Future<?>> entry: futureMap.entrySet()){
             entry.getValue().get();
        }
        for(ExecutionInstruction instruction:executionInstructions) {
            if (instruction != null) {
                retVal.put(instruction.getInstructionName(), instruction.getExecuteResult());
            }
        }
        this.retVal =retVal;
    }


    @Override
    public List<ExecutionInstruction> getChildren() {
        return executionInstructions;
    }


    @Override
    public String getInstuctionType() {
        if(needWait){
            return "blocked-parallel";
        }else{
            return "parallel";
        }

    }
}
